'''
Created on Jan 27, 2013

@author: kafkef
'''

import os

from PyQt4.QtWebKit import *
from PyQt4.QtGui import * 
from PyQt4.QtCore import Qt, QPoint, SIGNAL, QSettings, QStringList, qDebug, QUrl

import dexen
from dexen.system import client
from dexen.system.client import ui
from dexen.system.client.controller import timeIt
from dexen.system.client.controller import db_syncher
from dexen.system.client.db import JobInfo, ServerInfo
from dexen.system.client.ui.local.individuals import IndsPage
from dexen.system.client.ui.local.job_summary import SummaryPage
from dexen.system.client.ui.local.executions import ExecutionLogPage
from dexen import DEXEN_DIR
from mhlib import PATH
from dexen.system.client.ui.local.custom import CustomPage


svr_proxy = client.GetServerProxy()

_joblist_wdg = None
_jobinfo_tab_wdg = None

def GetJobListWdg():
    global _joblist_wdg
    if _joblist_wdg is None:
        _joblist_wdg = JobListWdg()
    return _joblist_wdg

def GetJobInfoTabWdg():
    global _jobinfo_tab_wdg
    if _jobinfo_tab_wdg is None:
        _jobinfo_tab_wdg = JobInfoTabWdg()
    return _jobinfo_tab_wdg

#===============================================================================
# Job List Widget  
#===============================================================================
class JobListWdgItem(QTreeWidgetItem):
    def __init__(self, parent, _list=None, job_info=None):
        if job_info is not None:
            assert isinstance(job_info, JobInfo)
            svr_info = job_info.get_svr_info()
            assert isinstance(svr_info, ServerInfo) 
            _list = [job_info.get_name(), job_info.get_start_time_str(), job_info.get_status(),
                     svr_info.get_name(), svr_info.get_start_time_str()]
        assert _list is not None

        qlist = (QStringList() << _list[0] << _list[1] << _list[2] << _list[3] << _list[4])
        super(JobListWdgItem, self).__init__(parent, qlist)
        self.obj = job_info

    def set_obj(self, job_info):
        if job_info is None: return
        assert isinstance(job_info, JobInfo)
        self.obj = job_info
        self.setText(2, job_info.get_status())

    def get_obj(self):
        return self.obj

    def on_disconnect(self):
        self.obj.set_status("UNKNOWN")
        self.setText(2, self.obj.get_status())

    def on_connect(self):
        pass

    def is_connected(self):
        job_info = self.obj
        assert isinstance(job_info, JobInfo)
        svr_info = job_info.get_svr_info()
        assert isinstance(svr_info, ServerInfo)
        return svr_info.is_connected()


class JobListWdg(QWidget):
    def __init__(self):
        super(JobListWdg, self).__init__()
        self._create_widgets()
        self.jobs = {} # JobInfo : QTreeWidgetItem
        self.db_syncher = db_syncher.GetDBSyncher()
    
    def _create_widgets(self):
        layout = QVBoxLayout()
        
        #self.titleLabel = QLabel("Local Site", self)
        self.treeWdg = QTreeWidget(self)
        self.treeWdg.setColumnCount(5)
        self.treeWdg.setHeaderLabels(["Job Name", "Start Time", "Status", "Server Address", "Server Start Time"])
        self.treeWdg.setContextMenuPolicy(Qt.CustomContextMenu)

        #layout.addWidget(self.titleLabel)
        layout.addWidget(self.treeWdg)
        
        self.setLayout(layout)
        
        self.restore_settings()
        
        self.connect(self.treeWdg, SIGNAL("customContextMenuRequested(const QPoint &)"),
             self._show_popup_menu)
        self.connect(self.treeWdg, SIGNAL("currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)"),
                     self._on_current_item_changed)

    def _show_popup_menu(self, point):
        from dexen.system.client.ui import main_window as mw        
        mainWindow = mw.GetMainWindow()
        assert isinstance(point, QPoint)
        assert isinstance(mainWindow, mw.MainWindow)
        
        item = self.treeWdg.itemAt(point)
        if item is None:
            return
        
        assert isinstance(item, JobListWdgItem)
        menu = QMenu(self.treeWdg)

        if item.is_connected():
            menu.addAction(mainWindow.synchDataAct)
        menu.addAction(mainWindow.deleteLocalJobAct)

        menu.exec_(self.treeWdg.mapToGlobal(point))

    def _on_current_item_changed(self, cur_item, prev_item):
        mainWindow = ui.GetMainWindow()
        if cur_item is not None:
            mainWindow.on_cur_local_job_changed(cur_item.get_obj())
        else:
            mainWindow.on_cur_local_job_changed(None)

    def add_servers(self, svr_info_list):
        for svr_info in svr_info_list:
            if svr_proxy.is_connected() and svr_info == svr_proxy.get_svr_info():
                svr_info.connect()
            for job_info in svr_info.get_jobs():
                self.add_job(job_info)
            self.update_jobs(svr_info.get_jobs())
    
    def current_job(self):
        item = self.treeWdg.currentItem()
        return item.get_obj()

    def add_job(self, job_info):
        assert isinstance(job_info, JobInfo)
        if self.jobs.has_key(job_info):
            return
        server_info = job_info.get_svr_info()
        assert isinstance(server_info, ServerInfo)

        self.jobs[job_info] = JobListWdgItem(self.treeWdg, job_info=job_info)
        
        if len(self.jobs.keys()) == 1:
            self.treeWdg.setCurrentItem(self.jobs[job_info])
        
        if not server_info.is_connected():
            self.jobs[job_info].on_disconnect()
        self.db_syncher.persist_job_info(job_info)

    def delete_job(self, job_info):
        assert self.jobs.has_key(job_info)
        job_item = self.jobs[job_info]
        for k in xrange(self.treeWdg.topLevelItemCount()):
            if job_item == self.treeWdg.topLevelItem(k):
                self.treeWdg.takeTopLevelItem(k)
                break
        del self.jobs[job_info]
        self.db_syncher.remove_job_info(job_info)

    def update_jobs(self, job_info_list):
        for job_info in job_info_list:
            if self.jobs.has_key(job_info):
                self.jobs[job_info].set_obj(job_info)
                self.db_syncher.persist_job_info(job_info, commit=False)
        self.db_syncher.commit()
        ui.GetMainWindow().on_jobs_info_updated(job_info_list)

    def on_close_file(self):
        self.jobs.clear()
        # Clear tree widget
        for _ in xrange(self.treeWdg.topLevelItemCount()):
            self.treeWdg.takeTopLevelItem(0)

    def on_connect(self, svr_info):
        pass
    
    def on_disconnect(self):
        for item in self.jobs.values():
            assert isinstance(item, JobListWdgItem)
            item.on_disconnect()
    
    def save_settings(self):
        settings = QSettings()
        settings.setValue("LocalJobListWdg/Header", self.treeWdg.header().saveState())
    
    def restore_settings(self):
        settings = QSettings()
        self.treeWdg.header().restoreState(settings.value("LocalJobListWdg/Header", "").toByteArray())    
    

#===============================================================================
# 
#===============================================================================
class JobInfoTabWdg(QTabWidget):
    def __init__(self):
        super(JobInfoTabWdg, self).__init__()
        self._create_pages()
        self._add_tabs()
        self.cur_job = None
        
    def _create_pages(self):
        self.summaryPage = SummaryPage()
        self.executionLogPage = ExecutionLogPage()
        self.indsPage = IndsPage()
        self.customPages = {}
        self.page_indexes = {}
        
    def _add_tabs(self):
        #self.addTab(self.summaryPage, "Summary")
        self.addTab(self.executionLogPage, "Execution Log")
        self.addTab(self.indsPage, "Individuals")
 
    def add_tab(self, tab_name):
        self.customPages[tab_name] = CustomPage(tab_name)
        self.page_indexes[tab_name] = self.addTab(self.customPages[tab_name], tab_name)
    
    def remove_tab(self, tab_name):
        self.removeTab(self.page_indexes[tab_name])
        del self.customPages[tab_name]
        del self.page_indexes[tab_name]
         
    def on_close_file(self):
        self.summaryPage.on_close_file()
        self.executionLogPage.on_close_file()
        self.indsPage.on_close_file()
    
    def on_connect(self):
        self.indsPage.on_connect()
        self.executionLogPage.on_connect()
    
    def on_disconnect(self):
        self.indsPage.on_disconnect()
        self.executionLogPage.on_disconnect()

    def delete_job(self, job_info):
        self.summaryPage.on_delete_job(job_info)
        self.executionLogPage.on_delete_job(job_info)
        self.indsPage.on_delete_job(job_info)
    
    def on_cur_job_changed(self, job_info):
        print "JobTabWidget:on_cur_job_changed"
        self.cur_job = job_info
        self.summaryPage.on_cur_job_changed(job_info)
        self.executionLogPage.on_cur_job_changed(job_info)
        self.indsPage.on_cur_job_changed(job_info)
        for customPage in self.customPages.values():
            customPage.on_cur_job_changed(job_info)
    
    @timeIt
    def on_actions_received(self, job_info, actions, islocal=False):
        self.executionLogPage.on_actions_received(job_info, actions)
    
    def on_jobs_info_updated(self, job_info_list):
        self.summaryPage.on_jobs_info_updated(job_info_list)
    
    def on_inds_updated(self, job_info, inds, islocal=False):
        self.indsPage.on_inds_updated(job_info, inds, islocal)
        for customPage in self.customPages.values():
            customPage.on_inds_updated(job_info, inds, islocal)



class LocalSiteWdg(QWidget):
    def __init__(self):
        super(LocalSiteWdg, self).__init__()
        self._create_widgets()

    def _create_widgets(self):
        layout = QVBoxLayout()
        self.jobListWdg = GetJobListWdg()
        self.jobInfoTabWdg = GetJobInfoTabWdg()
        
        self.splitter = QSplitter(Qt.Vertical)
        self.splitter.addWidget(self.jobListWdg)
        self.splitter.addWidget(self.jobInfoTabWdg)
        
        layout.addWidget(self.splitter)
        
        self.setLayout(layout)

    def toggle_tab(self, tab_name, checked):
        if checked:
            self.jobInfoTabWdg.add_tab(tab_name)
        else:
            self.jobInfoTabWdg.remove_tab(tab_name)

    def on_connect(self, svr_info=None):
        self.jobListWdg.on_connect(svr_info)
        self.jobInfoTabWdg.on_connect()
    
    def on_disconnect(self):
        self.jobListWdg.on_disconnect()
        self.jobInfoTabWdg.on_disconnect()

    def on_close_file(self):
        self.jobListWdg.on_close_file()
        self.jobInfoTabWdg.on_close_file()

    def current_job(self):
        return self.jobListWdg.current_job()

    def add_job(self, job_info):
        self.jobListWdg.add_job(job_info)
    
    def delete_job(self, job_info):
        self.jobListWdg.delete_job(job_info)
        self.jobInfoTabWdg.delete_job(job_info)
    
    def update_jobs(self, job_info_list):
        self.jobListWdg.update_jobs(job_info_list)
    
    def add_servers(self, svr_info_list):
        self.jobListWdg.add_servers(svr_info_list)

    def on_jobs_info_updated(self, job_info_list):
        self.jobInfoTabWdg.on_jobs_info_updated(job_info_list)
    
    def on_inds_updated(self, job_info, inds):
        self.jobInfoTabWdg.on_inds_updated(job_info, inds)
    
    def save_settings(self):
        self.jobListWdg.save_settings()
        settings = QSettings()
        settings.setValue("LocalSiteWdg/Splitter", self.splitter.saveState())    

    def restore_settings(self):
        self.jobListWdg.restore_settings()
        settings = QSettings()
        self.splitter.restoreState(settings.value("LocalSiteWdg/Splitter").toByteArray())        

